<script setup lang="ts">
import { unwrapApiBody, type ApiResponse } from '../../utils/http/factory'
import { extractFrontMatterDesc, stripFrontMatter } from '../../utils/markdown-front-matter'
import { buildPublicCanonicalUrl } from '../../utils/public-canonical-url'
import { safeExternalHref } from '../../utils/safe-external-href'
import { usePublicProfileLayoutMode } from '../../composables/usePublicHomeLayout'
import ShowcaseLayout from '../../components/public-home/ShowcaseLayout.vue'
import ReaderLayout from '../../components/public-home/ReaderLayout.vue'

definePageMeta({
  layout: 'public',
})

const route = useRoute()
const runtimeConfig = useRuntimeConfig()
const slug = computed(() => route.params.publicSlug as string)
const { mode } = usePublicProfileLayoutMode()

type PublicPostListItem = {
  title?: string | null
  excerpt?: string | null
  slug?: string | null
  publishedAt?: Date | string | null
}

type PublicTimelineItem = {
  title?: string | null
  bodyMarkdown?: string | null
}

type PublicRssListItem = {
  title?: string | null
  canonicalUrl?: string | null
  canonical_url?: string | null
}

type ModulePayload<T> = {
  items?: T[]
  total?: number
}

type Payload = {
  user: { publicSlug: string | null; nickname: string | null; avatar: string | null }
  bio: { markdown: string } | null
  links: { label: string; url?: string; href?: string; visibility: string; icon?: string }[]
  modules?: {
    posts?: ModulePayload<PublicPostListItem>
    timeline?: ModulePayload<PublicTimelineItem>
    reading?: ModulePayload<PublicRssListItem>
  }
  posts?: ModulePayload<PublicPostListItem>
  timeline?: ModulePayload<PublicTimelineItem>
  rssItems?: ModulePayload<PublicRssListItem>
}

type NormalizedModule<T> = {
  items: T[]
  total: number
}

function normalizeModule<T>(primary?: ModulePayload<T>, fallback?: ModulePayload<T>): NormalizedModule<T> {
  const source = primary ?? fallback
  return {
    items: Array.isArray(source?.items) ? source.items : [],
    total: typeof source?.total === 'number' ? source.total : 0,
  }
}

function socialHref(link: { url?: string; href?: string }): string | undefined {
  const value = link.url ?? link.href
  return safeExternalHref(value, { allowMailto: true })
}

const { data, pending, error } = await useAsyncData(
  () => `public-profile-${slug.value}`,
  async () => {
    const res = await $fetch<ApiResponse<Payload>>(`/api/public/profile/${encodeURIComponent(slug.value)}`)
    return unwrapApiBody(res)
  },
  { watch: [slug] },
)

const postsModule = computed(() => normalizeModule(data.value?.modules?.posts, data.value?.posts))
const timelineModule = computed(() => normalizeModule(data.value?.modules?.timeline, data.value?.timeline))
const readingModule = computed(() => normalizeModule(data.value?.modules?.reading, data.value?.rssItems))

const BIO_PREVIEW_MAX_CHARS = 140

const bioSummary = computed(() => {
  const md = data.value?.bio?.markdown
  if (!md?.trim()) {
    return ''
  }
  const desc = extractFrontMatterDesc(md)
  if (typeof desc === 'string' && desc.trim().length > 0) {
    return desc.trim()
  }
  return stripFrontMatter(md).trim()
})

const bioPreviewText = computed(() => {
  const plain = bioSummary.value.replace(/\s+/g, ' ').trim()
  if (!plain) {
    return ''
  }
  if (plain.length <= BIO_PREVIEW_MAX_CHARS) {
    return plain
  }
  return `${plain.slice(0, BIO_PREVIEW_MAX_CHARS).trimEnd()}...`
})

const hasBioPreview = computed(() => bioPreviewText.value.length > 0)
const isDetailedMode = computed(() => mode.value === 'detailed')

const socialLinks = computed(() =>
  (data.value?.links ?? [])
    .map(link => ({ ...link, safeHref: socialHref(link) }))
    .filter(link => typeof link.safeHref === 'string' && link.safeHref.length > 0) as Array<{
      label: string
      icon?: string
      safeHref: string
    }>,
)

const canonicalUrl = computed(() =>
  buildPublicCanonicalUrl(runtimeConfig.public.siteUrl, route.fullPath),
)

useHead(() => ({
  link: canonicalUrl.value
    ? [{ rel: 'canonical', href: canonicalUrl.value }]
    : [],
}))

usePageTitle(() => {
  const s = slug.value
  const d = data.value
  if (!d) {
    return [`@${s}`, '主页']
  }
  const name = d.user.nickname || (d.user.publicSlug ? `@${d.user.publicSlug}` : '') || s
  return [name, '主页']
})
</script>

<template>
  <div v-if="pending && !data" class="text-muted py-10">
    <UContainer>加载中…</UContainer>
  </div>
  <UContainer v-else-if="error && !data" class="py-10">
    <UAlert color="error" title="无法加载主页" />
  </UContainer>
  <UContainer
    v-else-if="data"
    :class="isDetailedMode ? 'max-w-6xl py-8 lg:py-10' : 'max-w-2xl py-10 sm:py-14'"
  >
    <ReaderLayout
      v-if="isDetailedMode"
      :slug="slug"
      :display-name="data.user.nickname || data.user.publicSlug || slug"
      :public-slug="data.user.publicSlug"
      :avatar="data.user.avatar"
      :bio-preview-text="bioPreviewText"
      :has-bio-preview="hasBioPreview"
      :social-links="socialLinks"
    />
    <ShowcaseLayout
      v-else
      :slug="slug"
      :display-name="data.user.nickname || data.user.publicSlug || slug"
      :public-slug="data.user.publicSlug"
      :avatar="data.user.avatar"
      :bio-preview-text="bioPreviewText"
      :has-bio-preview="hasBioPreview"
      :social-links="socialLinks"
      :posts-module="postsModule"
      :timeline-module="timelineModule"
      :reading-module="readingModule"
    />
  </UContainer>
</template>
